﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace JuCloudy.Service.Intreface.Pool
{
    public class ResourcePool<T> : IDisposable where T : class
    {
        private static ResourcePool<T> pool;
        IResourceProvider<T> resourceProvider;
        static int maxResource;

        public static int MaxResource
        {
            get { return ResourcePool<T>.maxResource; }

        }
        private ResourcePool(IResourceProvider<T> resourceProvider, int maxResource)
        {
            this.resourceProvider = resourceProvider;
            ResourcePool<T>.maxResource = maxResource;
            resources = new Dictionary<long, ResourceTag<T>>();
        }
        public int ResourceCount
        {
            get
            {
                return resources.Keys.Count;
            }
        }

        static object key3 = new object();
        /// <summary>
        /// 返回一个资源池，采用单件模式。
        /// </summary>
        /// <param name="resourceProvider"></param>
        /// <returns></returns>
        public static ResourcePool<T> Instance(IResourceProvider<T> resourceProvider, int maxResource)
        {
            if (pool == null)
            {
                lock (key3)
                {
                    if (pool == null)
                    {
                        pool = new ResourcePool<T>(resourceProvider, maxResource);
                    }
                }
            }
            return pool;
        }
        Dictionary<long, ResourceTag<T>> resources;
        /// <summary>
        ///从资源池中提取资源
        /// </summary>
        /// <param name="resourID">向资源用户输出的resourceID，返回资源时用它来返回特定资源</param>
        /// <returns></returns>
        public T GetResource(out long resourID)
        {
            T result = null;
            result = getFreeResource(out resourID);
            return result;
        }
        object key1 = new object();
        private T getFreeResource(out long resourID)
        {
            lock (key1)
            {
                foreach (long key in resources.Keys)
                {
                    if (!resources[key].InUse)
                    {
                        resources[key].InUse = true;
                        resourID = key;
                        return resources[key].Resource;
                    }
                }
                //申请新资源
                T res = resourceProvider.Request();
                if (res == null)//申请资源失败
                {
                    resourID = getNullResourceID();
                    return null;
                }
                else
                {
                    ResourceTag<T> tag = new ResourceTag<T>(res, true);
                    long id = newResourceID();
                    resources.Add(id, tag);
                    resourID = id;
                    return res;
                }
            }
        }

        private long getNullResourceID()
        {
            return -1;
        }
        /// <summary>
        /// 产生新的资源号
        /// </summary>
        /// <returns></returns>
        private long newResourceID()
        {
            return DateTime.Now.Ticks;
        }


        /// <summary>
        /// 返回资源
        /// </summary>
        /// <param name="resource">ref类型的参数，将在函数内部设为null，意味着返回后不能再用。</param>
        /// <param name="resourceID">获取资源时得到的那个resourceID。如果返回一个不正确的id,将抛出异常。</param>
        public void ReturnResource(ref T resource, long resourceID)
        {
            if (!resources.ContainsKey(resourceID))
            {
                throw new InvalidOperationException("试图归还一个非法的资源。");
            }
            returnRes(ref resource, resourceID);
        }
        object key2 = new object();
        private void returnRes(ref T resource, long resourceID)
        {
            T toDispose = null;
            lock (key2)
            {
                ResourceTag<T> tag = resources[resourceID];
                tag.InUse = false;
                resources.Remove(resourceID);//当前的id将作废，不能再用
                resource = null;//将当前的resource置空，不能再用
                if (resources.Keys.Count >= maxResource)//达到上限，将释放资源
                {
                    toDispose = resource;
                }
                else
                {
                    resources.Add(newResourceID(), tag);
                }
            }
            if (toDispose != null)
            {
                //toDispose.Dispose();
                resourceProvider.Dispose(toDispose);
            }
        }
        #region IDisposable 成员 及 析构方法

        public void Dispose()
        {
            Dispose(true);
        }
        ~ResourcePool()
        {
            Dispose(false);
        }
        public virtual void Dispose(bool isDisposing)
        {
            foreach (long key in resources.Keys)
            {
                resourceProvider.Dispose(resources[key].Resource);//释放资源
            }
            if (isDisposing)
            {
                key1 = null;
                key2 = null;
                key3 = null;
                resourceProvider = null;
            }
        }
        #endregion

    }
    internal class ResourceTag<T>
    {
        private T resource;

        internal T Resource
        {
            get { return resource; }
            set { resource = value; }
        }
        private bool inUse;

        internal bool InUse
        {
            get { return inUse; }
            set { inUse = value; }
        }
        public ResourceTag(T resource, bool inUse)
        {
            Resource = resource;
            InUse = inUse;
        }

    }
    /// <summary>
    /// 这个接口用来产生ResourcePool管理的资源，比如数据库连接对象等
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IResourceProvider<T> where T : class
    {
        /// <summary>
        /// 获得资源
        /// </summary>
        /// <returns>成功则返回资源对象，否则返回null</returns>
        T Request();
        void Dispose(T resource);
    }
}
